package com.seadragon.app.ksr.service.impl;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.seadragon.app.ksr.dao.AddressDao;
import com.seadragon.app.ksr.dao.CountyDao;
import com.seadragon.app.ksr.dao.PlaceDao;
import com.seadragon.app.ksr.dao.StateDao;
import com.seadragon.app.ksr.dao.ZipDao;
import com.seadragon.app.ksr.extractor.Extractor;
import com.seadragon.app.ksr.model.Address;
import com.seadragon.app.ksr.model.tiger.County;
import com.seadragon.app.ksr.model.tiger.Place;
import com.seadragon.app.ksr.model.tiger.State;
import com.seadragon.app.ksr.model.tiger.Zip;
import com.seadragon.app.ksr.normalizer.Normalizer;
import com.seadragon.app.ksr.parser.AddressParser;
import com.seadragon.app.ksr.service.AddressService;
import com.seadragon.app.ksr.service.GeocodeService;
import com.seadragon.app.ksr.util.CacheManager;

@Service("addressService")
@Transactional
public class AddressServiceImpl implements AddressService {
	private static Logger logger = LoggerFactory.getLogger(AddressServiceImpl.class);
	
	@Autowired
	private Normalizer addressLine1Normalizer;

	@Autowired
	private Extractor streetNumberExtractor;
	
	@Autowired
	private CountyDao countyDao;
	
	@Autowired
	private AddressDao addressDao;
	
	@Autowired
	private PlaceDao placeDao;
	
	@Autowired
	private ZipDao zipDao;
	
	@Autowired
	private StateDao stateDao;
	
	@Autowired
	private AddressParser addressParser;
	
	@Autowired
	private GeocodeService geocodeService;
		
	@Autowired
	private CacheManager cacheManager;
	
	@Override
	public boolean compare(Address address1, Address address2) {
		boolean result;
		if(address1.isValid()){
			if(address2.isValid()){
				if(address1.getStreetName().toUpperCase().equalsIgnoreCase(address2.getStreetName().toUpperCase())){
					result = true;
				}else{
					result = false;
				}
			}else{
				String streetNumber = streetNumberExtractor.extract(address2.getAddressLine1());
				String route = addressLine1Normalizer.normalize(address2.getAddressLine1().substring(streetNumber.length()).trim());
				if(streetNumber.equalsIgnoreCase(address1.getStreetNumber()) && route.indexOf(address1.getStreetName().toUpperCase()) >= 0){
					result = true;
				}else{
					result = false;
				}
			}
		}else{
			if(address2.isValid()){
				String streetNumber = streetNumberExtractor.extract(address1.getAddressLine1());
				String route = addressLine1Normalizer.normalize(address1.getAddressLine1().substring(streetNumber.length()).trim());
				if(streetNumber.equalsIgnoreCase(address2.getStreetNumber()) && route.indexOf(address2.getStreetName().toUpperCase()) >= 0){
					result = true;
				}else{
					result = false;
				}		
			}else{
				logger.info("This shouldn't happen: 2 addresses, none of them is geocoded");
				String streetNumber1 = streetNumberExtractor.extract(address1.getAddressLine1());
				String route1 = addressLine1Normalizer.normalize(address1.getAddressLine1().substring(streetNumber1.length()).trim());
				String streetNumber2 = streetNumberExtractor.extract(address2.getAddressLine1());
				String route2 = addressLine1Normalizer.normalize(address2.getAddressLine1().substring(streetNumber2.length()).trim());
				if(streetNumber1.equalsIgnoreCase(streetNumber2) && (route1.indexOf(route2) >= 0 || route2.indexOf(route1) >= 0)){
					result = true;
				}else{
					result = false;
				}
			}
		}
		logger.info("First address: " + address1.toString());
		logger.info("Second address: " + address2.toString());
		if(result)
			logger.info("Two addresses are same addresses!");
		else
			logger.info("They are not different addresses!");
		return result;
	}

	@Override
	public List<County> findAdjacentCounties(String countyName, String statefp) {
		List<County> counties = countyDao.findAdjacentCounties(countyName, statefp);
		for(County county : counties){
			county.setState(cacheManager.getStateNameByStateFp(statefp));
		}
		return counties;
	}

	@Override
	public List<Place> findAdjacentPlaces(String placeName, String statefp) {
		List<Place> places = placeDao.findAdjacentPlaces(placeName, statefp);
		for(Place place : places){
			place.setState(cacheManager.getStateNameByStateFp(statefp));
		}
		return places;
	}

	@Override
	public List<Zip> findAdjacentZips(String zipCode) {
		return zipDao.findAdjacentZips(zipCode);
	}

	@Override
	public List<State> findAdjacentStates(String stateName) {
		return stateDao.findAdjacentStates(stateName);
	}

	@Override
	public Address findById(long id) {
		return addressDao.findById(id);
	}
	
	@Override
	public boolean isValidAddress(Address address){
		if(exists(address)){
			return true;
		}
		geocodeService.geocode(address);
		addressDao.save(address);
		
		return address.isValid();
	}
	
	@Override
	public boolean exists(Address address){
		addressParser.parse(address);
		
		return addressDao.exists(address.getCompleteAddress());
	}
	
	@Override
	public List<Address> findAll(int offset, int pageSize) {
		return addressDao.findAll(offset, pageSize);
	}
	
	@Override
	public Address save(Address address){
		addressParser.parse(address);
		Address address2 = addressDao.findByCompleteAddress(address.getCompleteAddress());
		if(address2 != null){
			return address2;
		}
		
		geocodeService.geocode(address);
		addressDao.save(address);
		return address;
	}
	@Override
	public void geocodeAll(int pageSize){
		int offset = 0; 
		List<Address> addresses = null;
		do{
			addresses = addressDao.findAll(offset, pageSize);
			for(Address address : addresses){
				if(address.isValid()) continue;
				addressParser.parse(address);
				geocodeService.geocode(address);
				addressDao.update(address);
			}
			offset += pageSize;
		}while(addresses.size() == pageSize);

	}
}
